package com.boarsoft.dao.sharding.oceanbase.mybatis.inspcetor;

import com.alipay.sofa.dbp.RouteCondition;
import com.alipay.sofa.dbp.RouteParameters;
import com.boarsoft.dao.sharding.aspect.ShardingKeyContext;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.statement.Statement;
import net.sf.jsqlparser.util.TablesNamesFinder;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

import java.util.List;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

@Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class,
				ResultHandler.class }),
		@Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class, RowBounds.class,
				ResultHandler.class, CacheKey.class, BoundSql.class }) })
public class DbpRouteConditionInspector implements Interceptor {

	public static ConcurrentHashMap<String, String> statmentTable = new ConcurrentHashMap<>();

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		String uid = ShardingKeyContext.get();
        log.debug("DbpRouteConditionInspector ShardingKeyContext uid:{}",uid);
		if (uid == null||"-1".equals(uid)) {
			return invocation.proceed();
		}
		String tableName = calTableName(invocation);
        try{
		    RouteParameters routeParameters = RouteCondition.newRouteParameters();
			routeParameters.setGroupId(uid);
			routeParameters.setTargetTable(tableName + "_" + routeParameters.getGroupId());
            log.debug("DbpRouteConditionInspector ShardingKeyContext TargetTable:{}",routeParameters.getTargetTable());
           	return invocation.proceed();
		}catch(){
            log.error("targetTable:{} execute wrong:{}",
                    RouteCondition.getRouteParameters().getTargetTable(),e.getMessage());
            throw e;    
        }finally{
            RouteCondition.clear();
        }
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {

	}

	private String calTableName(Invocation invocation) throws JSQLParserException {
		MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
		String id = mappedStatement.getId();
		if (statmentTable.get(id) == null) {
			synchronized (statmentTable) {
				if (statmentTable.contains(id)) {
					return statmentTable.get(id);
				}
				final Object[] args = invocation.getArgs();
				MappedStatement ms = (MappedStatement) args[0];
				Object parameterObject = args[1];
				String processSql = ms.getBoundSql(parameterObject).getSql();
				Statement statement = CCJSqlParserUtil.parse(processSql);
				TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
				List<String> tableList = tablesNamesFinder.getTableList(statement);
				String tableName = tableList.get(0);
				statmentTable.put(id, tableName);
			}
		}
		return statmentTable.get(id);
	}

}
